/*
 * OrthogonalMatrix.h
 *
 * Created 6/1/2009 By Johnny Huynh
 *
 * Version 00.00.01 6/1/2009
 *
 * Copyright Information:
 * All content copyright © 2009 Johnny Huynh. All rights reserved.
 */
 
 // OrthogonalMatrix.h contains functions for orthogonal matrices

 #ifndef ORTHOGONALMATRIX_H
 #define ORTHOGONALMATRIX_H
 
 #include "macro.h"
 #include "Matrix.h"
 #include "Vector3.h"
 #include "OpenGL_Headers.h"
 #include <math.h>

 ///////////////////////////////// MATRIX 3 x 3 //////////////////////////////////////
 
 template <typename TYPENAME> class OrthogonalMatrix3;
 
 typedef OrthogonalMatrix3<GLfloat> OrthogonalMatrix3f;
 typedef OrthogonalMatrix3<GLdouble> OrthogonalMatrix3d;
 
 // The matrix based on the row and column
 //
 // | Xx Yx Zx |
 // | Xy Yy Zy |
 // | Xz Yz Zz |
 
 template <typename TYPENAME>
 class OrthogonalMatrix3 : public Matrix3<TYPENAME>
 {
 // Data Members
 
 // Local Functions
 public:
    OrthogonalMatrix3( const TYPENAME& mXx = ZERO, const TYPENAME& mYx = ZERO, const TYPENAME& mZx = ZERO,
                       const TYPENAME& mXy = ZERO, const TYPENAME& mYy = ZERO, const TYPENAME& mZy = ZERO,
                       const TYPENAME& mXz = ZERO, const TYPENAME& mYz = ZERO, const TYPENAME& mZz = ZERO );
    OrthogonalMatrix3( const OrthogonalMatrix3<TYPENAME>& m );
    OrthogonalMatrix3( const Matrix3<TYPENAME>& m );
    
 #ifdef VECTOR3_H
    OrthogonalMatrix3( const Vector3<TYPENAME>& u, const Vector3<TYPENAME>& v, const Vector3<TYPENAME>& t );
 #endif // VECTOR3_H
    
    ~OrthogonalMatrix3();
    inline OrthogonalMatrix3<TYPENAME> operator=( const OrthogonalMatrix3<TYPENAME>& m );
    inline OrthogonalMatrix3<TYPENAME> operator=( const Matrix3<TYPENAME>& m );
    inline OrthogonalMatrix3<TYPENAME>& invert();
    inline OrthogonalMatrix3<TYPENAME> getInverse() const;
    inline bool isIdentityMatrix() const;
    
 // Friend Functions
    template <typename TYPENAME> friend inline OrthogonalMatrix3<TYPENAME>& maintain( OrthogonalMatrix3<TYPENAME>& m );
 };



 /** LOCAL FUNCTIONS **/
 
 /**
  * Constructor
  */
 template <typename TYPENAME>
 OrthogonalMatrix3<TYPENAME>::OrthogonalMatrix3( 
                             const TYPENAME& mXx= ZERO, const TYPENAME& mYx = ZERO, const TYPENAME& mZx = ZERO,
                             const TYPENAME& mXy= ZERO, const TYPENAME& mYy = ZERO, const TYPENAME& mZy = ZERO,
                             const TYPENAME& mXz= ZERO, const TYPENAME& mYz = ZERO, const TYPENAME& mZz = ZERO )
                            : Matrix3<TYPENAME>( mXx, mXy, mXz,
                                                 mYx, mYy, mYz,
                                                 mZx, mZy, mZz )
 {
    maintain( *this );
 }
 
 /**
  * Alternative Constructor
  */
 template <typename TYPENAME>
 OrthogonalMatrix3<TYPENAME>::OrthogonalMatrix3( const OrthogonalMatrix3<TYPENAME>& m )
                            : Matrix3<TYPENAME>( m )
 {
    
 }
 
 /**
  * Alternative Constructor
  */
 template <typename TYPENAME>
 OrthogonalMatrix3<TYPENAME>::OrthogonalMatrix3( const Matrix3<TYPENAME>& m )
                            : Matrix3<TYPENAME>( m )
 {
    maintain( *this );
 }
 
 #ifdef VECTOR3_H
 /**
  * Alternative Constructor
  *
  *     [u.x v.x t.x
  * M =  u.y v.y t.y
  *      u.z v.z t.z]
  */
 template <typename TYPENAME>
 OrthogonalMatrix3<TYPENAME>::OrthogonalMatrix3( 
                            const Vector3<TYPENAME>& u, const Vector3<TYPENAME>& v, const Vector3<TYPENAME>& t )
                            : Matrix3<TYPENAME>( u, v, t )
 {
    maintain( *this );
 }
 #endif // VECTOR3_H
 
 /**
  * Destructor
  */
 template <typename TYPENAME>
 OrthogonalMatrix3<TYPENAME>::~OrthogonalMatrix3()
 {
 
 }
 
 /**
  * operator=() copies the values of the orthogonal 
  * matrix m and returns this matrix (referenced).
  * It returns a reference to itself, not matrix m.
  *
  * @param (const OrthogonalMatrix3<TYPENAME>&)m
  * @return OrthogonalMatrix3<TYPENAME>&
  */
 template <typename TYPENAME>
 inline OrthogonalMatrix3<TYPENAME> OrthogonalMatrix3<TYPENAME>::operator=( const OrthogonalMatrix3<TYPENAME>& m )
 {
    memcpy( this, &m, sizeof( OrthogonalMatrix3<TYPENAME> ) );
    return *this;
 }
 
 /**
  * operator=() copies the values of the matrix m, 
  * maintains itself, and returns this matrix (referenced).
  * It returns a reference to itself, not matrix m.
  *
  * @param (const OrthogonalMatrix3<TYPENAME>&)m
  * @return OrthogonalMatrix3<TYPENAME>&
  */
 template <typename TYPENAME>
 inline OrthogonalMatrix3<TYPENAME> OrthogonalMatrix3<TYPENAME>::operator=( const Matrix3<TYPENAME>& m )
 {
    memcpy( this, &m, sizeof( Matrix3<TYPENAME> ) );
    maintain( *this );
    return *this;
 }
 
 /**
  * invert() modifies this matrix to become the inverse
  * of this matrix.
  *
  * @return Matrix3<TYPENAME>&
  */
 template <typename TYPENAME>
 inline OrthogonalMatrix3<TYPENAME>& OrthogonalMatrix3<TYPENAME>::invert()
 {
    return this->transpose();
 }
 
 /**
  * getInverse() returns an inverted copy of this matrix.
  * Notice this matrix is not altered.
  * Instead, a newly created matrix is modified.
  *
  * @return Matrix3<TYPENAME>
  */
 template <typename TYPENAME>
 inline OrthogonalMatrix3<TYPENAME> OrthogonalMatrix3<TYPENAME>::getInverse() const
 {
    return this->getTranspose();
 }
 
 /**
  * isIdentityMatrix() returns true if this matrix is the identity matrix;
  * otherwise, false is returned.
  *
  * @return bool
  */
 template <typename TYPENAME>
 inline bool OrthogonalMatrix3<TYPENAME>::isIdentityMatrix() const
 {
    return Xx == ONE && Yy == ONE && Zz == ONE;
 }
 
 /** FRIEND FUNCTIONS **/
 
 /**
  * maintain() recomputes the orthogonal matrix by making its axes orthogonal
  * to one another and normalizing the axes. A reference to the specified
  * matrix is returned.
  *
  * @param (OrthogonalMatrix3<TYPENAME>&) m
  * @return OrthogonalMatrix3<TYPENAME>&
  */
 template <typename TYPENAME>
 inline OrthogonalMatrix3<TYPENAME>& maintain( OrthogonalMatrix3<TYPENAME>& m )
 {
    // new X-Axis = Y-Axis x Z-Axis
    m.Xx = (m.Yy*m.Zz) - (m.Yz*m.Zy);
    m.Xy = (m.Yz*m.Zx) - (m.Yx*m.Zz);
    m.Xz = (m.Yx*m.Zy) - (m.Yy*m.Zx);
    
    // normalize X-Axis
    TYPENAME mag( sqrt( (m.Xx*m.Xx) + (m.Xy*m.Xy) + (m.Xz*m.Xz) ) );
    // Prevent divide by zero
    if ( mag != ZERO )
    {
        m.Xx /= mag;
        m.Xy /= mag;
        m.Xz /= mag;
    }
    
    // new Y-Axis = Z-Axis x X-Axis
    m.Yx = (m.Zy*m.Xz) - (m.Zz*m.Xy);
    m.Yy = (m.Zz*m.Xx) - (m.Zx*m.Xz);
    m.Yz = (m.Zx*m.Xy) - (m.Zy*m.Xx);
    
    // normalize Y-Axis
    mag = sqrt( (m.Yx*m.Yx) + (m.Yy*m.Yy) + (m.Yz*m.Yz) );
    // Prevent divide by zero
    if ( mag != ZERO )
    {
        m.Yx /= mag;
        m.Yy /= mag;
        m.Yz /= mag;
    }
    
    // new Z-Axis = X-Axis x Y-Axis
    m.Zx = (m.Xy*m.Yz) - (m.Xz*m.Yy);
    m.Zy = (m.Xz*m.Yx) - (m.Xx*m.Yz);
    m.Zz = (m.Xx*m.Yy) - (m.Xy*m.Yx);
    
    return m;
 }
 
 #endif // ORTHOGONALMATRIX_H